#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <stdint.h>

int fd;
pthread_t tid_rx, tid_tx;
volatile int running = 1;

// Modbus CRC16计算
uint16_t modbus_crc16(const uint8_t *data, uint16_t length) {
    uint16_t crc = 0xFFFF;
    
    for (uint16_t i = 0; i < length; i++) {
        crc ^= data[i];
        for (uint8_t j = 0; j < 8; j++) {
            if (crc & 0x0001) {
                crc = (crc >> 1) ^ 0xA001;
            } else {
                crc >>= 1;
            }
        }
    }
    
    return crc;
}

// 接收线程
void *com_rx_thread(void *arg) {
    uint8_t rx_buf[256];
    int len;

    while (running) {
        len = read(fd, rx_buf, sizeof(rx_buf));
        if (len > 0) {
            printf("receive:");
            for (int i = 0; i < len; i++) {
                printf("%02X ", rx_buf[i]);
            }
            
            // 检查CRC
            if (len >= 2) {
                uint16_t received_crc = (rx_buf[len-1] << 8) | rx_buf[len-2];
                uint16_t calculated_crc = modbus_crc16(rx_buf, len-2);
                
                if (received_crc == calculated_crc) {
                    printf("[CRC OK]");
                } else {
                    printf("[CRC FAIL]");
                }
            }
            printf("\n");
        }
    }
    return NULL;
}

// 发送线程：从stdin读取16进制数据并自动添加CRC
void *com_tx_thread(void *arg) {
    uint8_t tx_buf[256];
    char input[512];
    int count;

    while (running) {
        if (fgets(input, sizeof(input), stdin) != NULL) {
            count = 0;
            char *token = strtok(input, " \n");
            while (token != NULL && count < sizeof(tx_buf) - 2) { // 留出CRC空间
                tx_buf[count++] = (uint8_t)strtol(token, NULL, 16);
                token = strtok(NULL, " \n");
            }
            
            if (count > 0) {
                // 自动添加CRC
                uint16_t crc = modbus_crc16(tx_buf, count);
                tx_buf[count++] = crc & 0xFF;
                tx_buf[count++] = (crc >> 8) & 0xFF;
                
                write(fd, tx_buf, count);
                printf("sent %d bytes (with CRC): ", count);
                for (int i = 0; i < count; i++) {
                    printf("%02X ", tx_buf[i]);
                }
                printf("\n");
            }
        }
    }
    return NULL;
}

void on_sigint(int sig) {
    running = 0;
    pthread_cancel(tid_rx);
    pthread_cancel(tid_tx);
    if (fd > 0) close(fd);
    printf("\nSerial monitor stopped.\n");
    exit(0);
}

int main(int argc, char *argv[]) {
    struct termios newtio;
    const char *com = (argc == 1) ? "/dev/ttyUSB0" : argv[1];

    fd = open(com, O_RDWR | O_NOCTTY);
    if (fd == -1) {
        perror("open error");
        exit(1);
    }

    // 串口配置
    memset(&newtio, 0, sizeof(newtio));
    newtio.c_cflag = B115200 | CS8 | CLOCAL | CREAD;
    newtio.c_iflag = 0;
    newtio.c_oflag = 0;
    newtio.c_lflag = 0;
    newtio.c_cc[VMIN] = 0;
    newtio.c_cc[VTIME] = 10;
    tcflush(fd, TCIFLUSH);
    tcsetattr(fd, TCSANOW, &newtio);

    signal(SIGINT, on_sigint);

    pthread_create(&tid_rx, NULL, com_rx_thread, NULL);
    pthread_create(&tid_tx, NULL, com_tx_thread, NULL);
    pthread_join(tid_rx, NULL);
    pthread_join(tid_tx, NULL);

    close(fd);
    return 0;
}
